package com.liming.cart.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.liming.auth.client.UserAuthClient;
import com.liming.cart.mapper.CartMircoMapper;
import com.liming.cart.service.CartItemMircoService;
import com.liming.cart.service.CartMircoService;
import com.liming.common.enums.CartStatusEnum;
import com.liming.common.enums.OperationTypeEnum;
import com.liming.common.exception.BusinessException;
import com.liming.common.pojo.dto.authinfo.UserActivityAddDTO;
import com.liming.common.pojo.dto.cart.AddItemToCartDTO;
import com.liming.common.pojo.entity.cart.Cart;
import com.liming.common.pojo.entity.cart.CartItem;
import com.liming.common.pojo.vo.cart.openfeign.FeignProductInCartVO;
import com.liming.common.pojo.vo.product.BaseProductVO;
import com.liming.common.pojo.vo.product.openfeign.FeignProductVO;
import com.liming.common.util.ThreadLocalUtils;
import com.liming.common.util.mybatisplus.MPUtil;
import com.liming.product.client.ProductClient;
import lombok.AllArgsConstructor;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;

import static com.liming.common.constant.SystemConstants.ID;

/**
 * @author liming
 * @date 2025/1/22
 */
@Service
@AllArgsConstructor
public class CartMircoServiceImpl extends ServiceImpl<CartMircoMapper, Cart>
        implements CartMircoService, MPUtil<Cart> {

    private final CartItemMircoService cartItemMircoService;
    private final ProductClient productClient;
    private final UserAuthClient userAuthClient;

    @Override
    @Transactional
    public List<FeignProductInCartVO> getProductInCart() {
        String userIdString = ThreadLocalUtils.get(ID);
        Assert.notNull(userIdString, () -> new BusinessException("用户ID为传入 - 请检查"));
        Long userId = Long.parseLong(userIdString);

        // 查询用户购物车
        Cart userCart = lambdaQuery()
                .eq(Cart::getUserId, userId)
                .eq(Cart::getState, CartStatusEnum.ACTIVE.getCode())
                .one();
        // 购物车ID
        Long cartId = userCart.getId();
        // 购物车中商品信息
        List<CartItem> cartItems = cartItemMircoService.lambdaQuery().eq(CartItem::getCartId, cartId).list();
        // 查询所有商品信息
        List<Long> productIds = cartItems.stream().map(CartItem::getProductId).toList();
        if (CollUtil.isEmpty(productIds)) {
            return Collections.emptyList();
        }
        List<FeignProductVO> feignProductVOS = productClient.listByIds(productIds);
        Assert.isTrue(feignProductVOS.size() == productIds.size(), () -> new BusinessException("商品信息获取失败"));
        Map<Long, FeignProductVO> productVOMap = feignProductVOS.stream()
                .collect(Collectors.toMap(BaseProductVO::getId, feignProductVO -> feignProductVO));
        Boolean cartItemPriceUpd = false;
        for (CartItem cartItem : cartItems) {
            Long productId = cartItem.getProductId();
            FeignProductVO feignProductVO = productVOMap.get(productId);
            // 购物车中商品价格
            Integer productPrice = cartItem.getPrice();
            // 商品价格
            Integer price = feignProductVO.getPrice();
            if (ObjectUtil.notEqual(productPrice, price)) {
                // 说明商品价格已经修改 需要即时更新购物车中商品价格
                cartItem.setPrice(price);
                cartItemPriceUpd = true;
            }
        }
        // 若购物车中有商品价格修改 需要更新购物车中商品价格
        if (ObjectUtil.equal(cartItemPriceUpd, true)) {
            Assert.isTrue(cartItemMircoService.updateBatchById(cartItems), () -> new BusinessException("更新购物车商品价格失败"));
        }

        CompletableFuture.runAsync(() -> {
            UserActivityAddDTO userActivityAddDTO = new UserActivityAddDTO();
            userActivityAddDTO.setUserId(userId);
            userActivityAddDTO.setOperationType(OperationTypeEnum.SHOW_CART.getCode());
            userAuthClient.addUserActivity(userActivityAddDTO);
        });

        return cartItems.stream().map((cartItem)->{
            FeignProductVO feignProductVO = productVOMap.get(cartItem.getProductId());
            FeignProductInCartVO vo = new FeignProductInCartVO();
            vo.setId(cartItem.getId());
            vo.setProductId(cartItem.getProductId());
            vo.setPrice(cartItem.getPrice());
            vo.setQuantity(cartItem.getQuantity());
            vo.setProductName(feignProductVO.getName());
            vo.setMainImageUrl(feignProductVO.getMainImageUrl());
            return vo;
        }).toList();
    }

    @Override
    public Boolean addItemToCart(AddItemToCartDTO dto) {
        String userIdString = ThreadLocalUtils.get(ID);
        Assert.notNull(userIdString, () -> new BusinessException("用户ID未传入 - 请检查"));
        long userId = Long.parseLong(userIdString);
        // 确保用户存在购物车
        Cart userCart = lambdaQuery()
                .eq(Cart::getUserId, userId)
                .eq(Cart::getState, CartStatusEnum.ACTIVE.getCode())
                .one();
        userCart = ensureUCExists(userCart, userId);
        // 往购物车中添加商品
        // 1. 判断购物车中是否有该商品
        Long productId = dto.getProductId();
        CartItem cartItem = cartItemMircoService.lambdaQuery()
                .eq(CartItem::getCartId, userCart.getId())
                .eq(CartItem::getProductId, productId)
                .one();
        // 2.1 若没有 新增商品
        if (ObjectUtil.isNull(cartItem)) {
            cartItem = new CartItem();
            BeanUtils.copyProperties(dto, cartItem);
            cartItem.setCartId(userCart.getId());
            Assert.isTrue(cartItemMircoService.save(cartItem), () -> new BusinessException("新增购物车商品失败"));
            CompletableFuture.runAsync(() -> {
                UserActivityAddDTO userActivityAddDTO = new UserActivityAddDTO();
                userActivityAddDTO.setUserId(userId);
                userActivityAddDTO.setOperationType(OperationTypeEnum.ADD_CART.getCode());
                userActivityAddDTO.setKeyId(productId);
                userAuthClient.addUserActivity(userActivityAddDTO);
            });
            return true;
        }
        // 2.2 若有 更新商品数量
        Assert.isTrue(cartItemMircoService.addItemQuantity(cartItem, productId, dto.getQuantity()), () -> new RuntimeException("更新购物车商品数量失败"));
        CompletableFuture.runAsync(() -> {
            UserActivityAddDTO userActivityAddDTO = new UserActivityAddDTO();
            userActivityAddDTO.setUserId(userId);
            userActivityAddDTO.setOperationType(OperationTypeEnum.ADD_CART.getCode());
            userActivityAddDTO.setKeyId(productId);
            userAuthClient.addUserActivity(userActivityAddDTO);
        });
        return true;
    }

    @Override
    public Cart getUserCart() {
        return lambdaQuery().eq(Cart::getUserId, ThreadLocalUtils.get(ID)).eq(Cart::getState, CartStatusEnum.ACTIVE.getCode()).one();
    }


    @NotNull
    private Cart ensureUCExists(Cart userCart, long userId) {
        if (ObjectUtil.isNull(userCart)) {
            // 若用户没有购物车 新增
            userCart = new Cart();
            userCart.setUserId(userId);
            userCart.setState(CartStatusEnum.ACTIVE.getCode());
            Assert.isTrue(save(userCart), () -> new BusinessException("新增用户购物车失败"));
        }
        return userCart;
    }
}
